//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <drvpub.h>
#include <lcd.h>
#include <vkey.h>
#include "..\display\font.h"
#include "..\display\_display.h"
#include "..\keypad\keypad.h"
#include "gpostern.h"
#include <bulverde.h>

EXTERN_C Display_Address g_dispAddr;

//use to record the state of GPostern
UInt32 g_GState;

//use to show the limit
Position g_PosTLPoint;
Position g_PosRBPoint;
//the Cursor position
Position g_PosCursor;

static UInt32 g_iCurrentHead = 0;
static UInt32 g_iCurrent = 0;
static UInt32 g_iMenuItemSum = 0;
UInt32 g_ShowItemSum;

EXTERN_C void GInitPrintLimit()
{
    g_PosTLPoint.x = CHAR_WIDTH;
    g_PosTLPoint.y = CHAR_HEIGHT;
    g_PosRBPoint.x = LCD_WIDTH - CHAR_WIDTH;
    g_PosRBPoint.y = LCD_HEIGHT - CHAR_HEIGHT;
    g_PosCursor.x = g_PosTLPoint.x;
    g_PosCursor.y = g_PosTLPoint.y;
}



EXTERN_C void LCDSetPixel(UInt32 lX, UInt32 lY, UInt32 cColor)
{
    // Make sure the specified pixel is valid.
    if( (lX >= LCD_WIDTH) || (lY >= LCD_HEIGHT) ) return;

    *(UInt32*)((UInt32 *)g_dispAddr.vaBasePlane + ((lX + lY * LCD_WIDTH))) = cColor;
}

// LCDPrintChar prints a character at the specified location.
EXTERN_C void LCDPrintChar(char cChar, UInt32 lX, UInt32 lY, UInt32 cColor)
{
    UInt32 lIdx1, lIdx2;
    UInt8 cFontLine;
    UInt32 x,y;
    UInt32 v;
    // The character font is 8x8, so loop through each row of the font.
    for(lIdx1 = 0; lIdx1 < CHAR_HEIGHT; lIdx1++)
    {
        // Read the byte which describes this row of the font.
        cFontLine = FontData[(cChar << 3) + lIdx1];

        // Loop through each column of this row.
        y = lY + lIdx1;
        for(lIdx2 = 0; lIdx2 < CHAR_WIDTH; cFontLine <<= 1, lIdx2++) {
            x = lX + lIdx2;
            v = (cFontLine & 0x80) ? cColor : 0;//Check most bit
            LCDSetPixel(x, y, v);
        }
    }
}
//BUGBUG:there has not implemented the screen turn page func
EXTERN_C void GPrintf(char *psz)
{
    if(NULL == psz)
        return;
    while (*psz != '\0') {
        if (g_PosCursor.x >= g_PosRBPoint.x || g_PosCursor.x < g_PosTLPoint.x) {
            g_PosCursor.x = g_PosTLPoint.x;
        }
        if (g_PosCursor.y >= g_PosRBPoint.y || g_PosCursor.y < g_PosTLPoint.y) {
            g_PosCursor.y = g_PosTLPoint.y;
        }
        if('\n' == *psz) {
            g_PosCursor.x = g_PosTLPoint.x;
            g_PosCursor.y = g_PosCursor.y + CHAR_ROW_HEIGHT;
            psz++;
            continue;
        }
        LCDPrintChar(*psz, g_PosCursor.x, g_PosCursor.y, cwhite);
        psz++;
        g_PosCursor.x = g_PosCursor.x + CHAR_WIDTH;
        if (g_PosCursor.x >= g_PosRBPoint.x) {
            g_PosCursor.x = g_PosTLPoint.x;
            g_PosCursor.y = g_PosCursor.y + CHAR_ROW_HEIGHT;
        }
        if (g_PosCursor.y >= g_PosRBPoint.y) {
            //add the screen operation here
        }
    }
}


//LCD Grafix
//JUST LEFT TO RIGHT
EXTERN_C void DrawHorizontalLine(Position *pStart, Position *pEnd, UInt32 cColor)
{
    UInt32 i;
    for (i = pStart->x; i < pEnd->x; i++) {
        *(UInt32*)((UInt32 *)g_dispAddr.vaBasePlane + ((i + pStart->y * LCD_WIDTH))) = cColor;
    }
}
//JUST TOP TO BOTTOM
EXTERN_C void DrawVerticalLine(Position *pStart, Position *pEnd, UInt32 cColor)
{
    UInt32 i;
    for (i = pStart->y; i < pEnd->y; i++) {
        *(UInt32*)((UInt32 *)g_dispAddr.vaBasePlane + ((pStart->x + i * LCD_WIDTH))) = cColor;
    }
}

EXTERN_C void DrawRect(Position *pTLPoint, Position *pRBPoint, UInt32 cColor)
{
    UInt32 i, j;
    for (j = pTLPoint->y; j < pRBPoint->y; j++) {
        for(i = pTLPoint->x; i < pRBPoint->x; i++) {
            *(UInt32*)((UInt32 *)g_dispAddr.vaBasePlane + ((i + j * LCD_WIDTH))) = cColor;
        }
    }
}

//menu
EXTERN_C void GDrawFrame()
{
    Position ps, pe;
    //draw background
    ps.x = 0;
    ps.y = 0;
    pe.x = LCD_WIDTH;
    pe.y = LCD_HEIGHT;
    DrawRect(&ps, &pe, cblack);
    //draw the line
    pe.x = 0;
    DrawVerticalLine(&ps, &pe, cwhite);
    ps.x = LCD_WIDTH - 1;
    pe.x = LCD_WIDTH - 1;
    DrawVerticalLine(&ps, &pe, cwhite);
    //draw the row
    ps.x = 0;
    pe.y = 0;
    DrawHorizontalLine(&ps, &pe, cwhite);
//    DrawHorizontalLine(&ps, &pe, cwhite);
    ps.y = LCD_HEIGHT - 1;
    pe.y = LCD_HEIGHT - 1;
    DrawHorizontalLine(&ps, &pe, cwhite);
}

ECode GFunctest(void* argv);
ECode VibrationTest(void *argv);
ECode LCDTest(void *argv);
ECode AodioPlayTest(void *argv);
ECode CameraTest(void *argv);
GPstMenuItem g_GPstMainMenuItem[MAX_POSTERN_CELL] =
{
    {VibrationTest, "Vibration Test", "Vibration Test"},
    {AodioPlayTest,     "Audio Test", "discri3"},
    {CameraTest,     "Camera Test", "discri9"},
    {GFunctest,     "Keypad Test", "discri4"},
    {GFunctest,     "Touch Test", "discri5"},
    {GFunctest,     "fun6", "discri6"},
    {GFunctest,     "fun7", "discri7"},
    {LCDTest,       "LCD Test", "LCDTest"},
    {GFunctest,     "GPrintfTest", "GPrintfTest"},
    {GFunctest,     "fun8", "discri8"},
    {GFunctest,     "fun0", "discri0"}
};
GPstMenuItem *g_GPstMenuItem = g_GPstMainMenuItem;

EXTERN_C void __cdecl GDrawChoicedFrame(Boolean write)
{
    Position ps, pe;
    UInt32 cColor;
    UInt32 iCur = g_iCurrent - g_iCurrentHead;
    if(write) {
        cColor = cwhite;
    } else {
        cColor = cblack;
    }
    //draw the line
    ps.x = MENU_CHOICE_LENTH;
    ps.y = FUNC_ROW_HEIGHT * iCur + MENU_CHOICE_LENTH;
    pe.x = MENU_CHOICE_LENTH;
    pe.y = FUNC_ROW_HEIGHT * (iCur + 1) - MENU_CHOICE_LENTH;
    DrawVerticalLine(&ps, &pe, cColor);
    ps.x = LCD_WIDTH - MENU_CHOICE_LENTH;
    pe.x = LCD_WIDTH - MENU_CHOICE_LENTH;
    DrawVerticalLine(&ps, &pe, cColor);
    //draw the row
    ps.x = MENU_CHOICE_LENTH;
    pe.y = FUNC_ROW_HEIGHT * iCur + MENU_CHOICE_LENTH;
    DrawHorizontalLine(&ps, &pe, cColor);
    ps.y = FUNC_ROW_HEIGHT * (iCur + 1) - MENU_CHOICE_LENTH;
    pe.y = FUNC_ROW_HEIGHT * (iCur + 1) - MENU_CHOICE_LENTH;
    DrawHorizontalLine(&ps, &pe, cColor);
}

EXTERN_C void __cdecl GDrawMenuItem()
{
    //change the GPrintf limit
    g_PosTLPoint.x = LCD_WIDTH / 4;
    g_PosRBPoint.x = (LCD_WIDTH / 4) * 3;
    Position ps, pe;
    ps.x = 0;
    ps.y = FUNC_ROW_HEIGHT;
    pe.x = LCD_WIDTH;
    pe.y = FUNC_ROW_HEIGHT;
    UInt32 i;
    for (i = 0; i < g_ShowItemSum
                && (g_iCurrentHead + i) < g_iMenuItemSum
                && g_GPstMenuItem[g_iCurrentHead + i].PosternCell != NULL; i++) {
        //BUGBUG:suppose the length of name string is less than LCD_WIDTH
        g_PosCursor.x = LCD_WIDTH / 2
                - ((strlen(g_GPstMenuItem[g_iCurrentHead + i].szName) / 2) * CHAR_WIDTH);
        g_PosCursor.y = i * FUNC_ROW_HEIGHT + FUNC_ROW_HEIGHT / 2;
        GPrintf(g_GPstMenuItem[g_iCurrentHead + i].szName);
        DrawHorizontalLine(&ps, &pe, cwhite);
        ps.y = ps.y + FUNC_ROW_HEIGHT;
        pe.y = pe.y + FUNC_ROW_HEIGHT;
    }
    GDrawChoicedFrame(TRUE);
    //revert the Gprintf limit
    GInitPrintLimit();
}


//turn = true ->pagedown
EXTERN_C void __cdecl GTurnPage(bool turn)
{
    if (turn) {
        g_iCurrentHead = g_iCurrentHead + g_ShowItemSum;
    } else {
        g_iCurrentHead = g_iCurrentHead - g_ShowItemSum;
    }
    if(MENU0 == (g_GState & 0x1)){
        GDrawFrame();
        GDrawMenuItem();
    } else if(FUNC == g_GState)
    {
        //add screen turn page func
    }
}

EXTERN_C void __cdecl GChangeItem(bool turn)
{
    if(MENU0 == (g_GState & 0x1)){
        int temp;
        if (turn) {
            temp = g_iCurrent + 1;
            if ((UInt32)temp < g_iMenuItemSum
                && g_GPstMenuItem[temp].PosternCell != NULL) {
                if((g_iCurrent % g_ShowItemSum) == (g_ShowItemSum - 1)){
                    g_iCurrent = temp;
                    GTurnPage(TRUE);
                }else{
                    GDrawChoicedFrame(FALSE);
                    g_iCurrent = temp;
                    GDrawChoicedFrame(TRUE);
                }
            }
        } else {
            temp = (int)(g_iCurrent - 1);
            if ((temp >= 0)
                && g_GPstMenuItem[temp].PosternCell != NULL) {
                if((g_iCurrent % g_ShowItemSum) == 0){
                    g_iCurrent = temp;
                    GTurnPage(FALSE);
                }else{
                    GDrawChoicedFrame(FALSE);
                    g_iCurrent = temp;
                    GDrawChoicedFrame(TRUE);
                }
            }
        }
    } else if(FUNC == g_GState)
    {
        //add screen turn page func
    }
}

/****************************************************************************
//Access virkey for TF smartphone
VK_F1               0x70    // F1 key
VK_F2               0x71    // F2 key
VK_F3               0x72    // F3 key
VK_F4               0x73    // F4 key
VK_F5               0x74    // F5 key
VK_VOLUME_MUTE         0xAD    // Volume Mute key
VK_VOLUME_DOWN         0xAE    // Volume Down key
VK_VOLUME_UP           0xAF    // Volume Up key
VK_MEDIA_NEXT_TRACK    0xB0    // Next Track key
VK_MEDIA_PREV_TRACK    0xB1    // Previous Track key
VK_MEDIA_STOP          0xB2    // Stop Media key
VK_MEDIA_PLAY_PAUSE    0xB3    // Play/Pause Media key
VK_PHONE_CALL          0x88    // call
VK_PHONE_HANG_UP       0x89    // hang up
VK_PHONE_SELECT_LEFT   0x8A    // select left
VK_PHONE_SELECT_RIGHT  0x8B    // select right
VK_CAMERA              0x8C
VK_RECORDER            0x8D
VK_HOME                0x8E

//lstatus
//                               Extended Key Status
//                               |
//         31|30|29|28|27|26|25|24|23|..|16|15|..|00
//          |
//          |
//          |
//          PressOrRelease
//
*****************************************************************************
*/

EXTERN_C void GInitMenuInfo(GPstMenuItem *MenuItem, UInt32 iMenuItemSum)
{
    g_iCurrentHead = 0;
    g_iCurrent = 0;
    g_GPstMenuItem = MenuItem;
    g_iMenuItemSum = iMenuItemSum;
}

GFuncCell g_EndProc;

Boolean GBDKeyPadProc(void *pParam)
{
    KPD_DATA *pPT = (KPD_DATA *)pParam;
    if(KEYDOWN != pPT->status) {
        return TRUE;
    }
    switch(pPT->keycode){

    case VK_VOLUME_DOWN:
        if (MENU0 == (g_GState & 0x1)) {
            GChangeItem(TRUE);
        }
        break;
    case VK_VOLUME_UP:
        if (MENU0 == (g_GState & 0x1)) {
            GChangeItem(FALSE);
        }
        break;
    case VK_RECORDER/*VK_SELECT*/:
        if (MENU0 == (g_GState & 0x1)) {
            g_GPstMenuItem[g_iCurrent].PosternCell(NULL);
        }
        break;
    case VK_CAMERA/*VK_END*/:
        if (MENU0 == g_GState) {
            return FALSE;
        }
        else if (MENU1 == g_GState) {
            GInitMenuInfo(g_GPstMainMenuItem, MAX_POSTERN_CELL);
            GDrawFrame();
            GDrawMenuItem();
            g_GState = MENU0;
        } else if (FUNC == g_GState) {
            if (NULL != g_EndProc) {
                g_EndProc(NULL);
            }
            g_EndProc = NULL;
            GDrawFrame();
            GDrawMenuItem();
            if (g_GPstMenuItem == g_GPstMainMenuItem) g_GState = MENU0;
            else g_GState = MENU1;//all fuc state must in sub menu
        }
        break;
    default:
        break;
    }
    return TRUE;
}

EXTERN_C void __cdecl GInitPostern()
{
    g_GState = MENU0;
    GInitPrintLimit();
    g_ShowItemSum = LCD_HEIGHT / FUNC_ROW_HEIGHT;
}

ECode GPosternMain(void *argv)
{
    void *CurrentBuffer = Drv_AllocBuffer(BASEPLANE_FB_PAGES, 0);
    memcpy (CurrentBuffer, (void *)g_dispAddr.vaBasePlane, BASEPLANE_FB_PAGES<<12);
    GInitPostern();
    GDrawFrame();
    GInitMenuInfo(g_GPstMainMenuItem, MAX_POSTERN_CELL);
    GDrawMenuItem();
    Boolean fRun = TRUE;
    ECode ec;
    IDeviceDriver *pEventQueues = NULL;
    ec = DzFindService(
            L"device:eq0", (PInterface *)&pEventQueues);
    if (FAILED(ec)) {
        kprintf("GPostern: Can not find pEventQueues service!\n");
        fRun = FALSE;
    }

    Int32 nControlCode = EVENT_KEYPAD;
    nControlCode = (nControlCode << 16)|10;
    MemoryBuf_<8> ebbData;
    ec = pEventQueues->Control(nControlCode, NULL_MEMORYBUF, &ebbData, NULL);
//    if (FAILED(ec)) {
//        kprintf("GPostern: Can not find free the pEventQueues!\n");
//    }

    Boolean bPreemption = DzDisablePreemption();

    while(fRun) {
        ebbData.SetUsed(0);
//        ec = pEventQueues->Read(0x80000000/*Async*/+EVENT_KEYPAD, 0, ebbData, NULL);
        ec = pEventQueues->Read(EVENT_KEYPAD, 0, &ebbData, NULL);
        if (SUCCEEDED(ec)) {
            fRun = GBDKeyPadProc(ebbData.GetPayload());
        }
    }

    DzRestorePreemption(bPreemption);

    memcpy ((void *)g_dispAddr.vaBasePlane, CurrentBuffer, BASEPLANE_FB_PAGES<<12);
    Drv_FreeBuffer(CurrentBuffer, BASEPLANE_FB_PAGES);
    return NOERROR;
}

IThread *g_pGPosternThd = NULL;
EXTERN_C void __cdecl GPosternEntry()
{
    DzCreateKernelThread(GPosternMain,
                     NULL, CreateThreadFlag_System,
                     ThreadPriorityRank_SoftRealTime, &g_pGPosternThd);
}

